using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
using Serilog;
using Serilog.Events;
using Volo.Abp;
using Volo.Abp.AspNetCore;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Serilog;
using Volo.Abp.Autofac;
using Volo.Abp.Auditing;
using Volo.Abp.Modularity;
using Volo.Abp.Swashbuckle;
using Volo.Abp.AspNetCore.ExceptionHandling;
using Volo.Abp.AspNetCore.Mvc.ExceptionHandling;
using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.PostgreSql;
using Volo.Abp.BackgroundJobs;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.AspNetCore.Authentication.JwtBearer;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.MultiTenancy;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Identity.AspNetCore;
using Volo.Abp.Threading;
using Matrix.Notification.HttpApi.HealthChecks;
using Matrix.Notification.EntityFrameworkCore;

namespace Matrix.Notification.HttpApi.Host
{
    [DependsOn(
        typeof(AbpAutofacModule),
        typeof(AbpAspNetCoreMvcModule),
        typeof(AbpAspNetCoreAuthenticationJwtBearerModule),
        typeof(AbpAspNetCoreMultiTenancyModule),
        typeof(AbpIdentityAspNetCoreModule),
        typeof(AbpSwashbuckleModule),
        typeof(AbpAspNetCoreSerilogModule),
        typeof(AbpEntityFrameworkCorePostgreSqlModule),
        typeof(AbpBackgroundJobsModule),
        typeof(AbpAuditingModule),
        typeof(MatrixNotificationHttpApiModule),
        typeof(MatrixNotificationEntityFrameworkCoreModule)
    )]
    public class MatrixNotificationHttpApiHostModule : AbpModule
    {
        public override void PreConfigureServices(ServiceConfigurationContext context)
        {
            PreConfigure<IMvcBuilder>(mvcBuilder =>
            {
                mvcBuilder.AddConventions(options =>
                {
                    options.Conventions.Add(new RouteTokenTransformerConvention(
                        new SlugifyParameterTransformer()));
                });
            });
        }

        public override void ConfigureServices(ServiceConfigurationContext context)
        {
            var configuration = context.Services.GetConfiguration();
            var hostingEnvironment = context.Services.GetHostingEnvironment();

            ConfigureAuthentication(context, configuration);
            ConfigureLocalization();
            ConfigureSwagger(context, configuration);
            ConfigureCors(context, configuration);
            ConfigureHealthChecks(context, configuration);
            ConfigureDataProtection(context, configuration);
            ConfigureDistributedCache(context, configuration);
            ConfigureDatabase(context, configuration);
            ConfigureAuditing(context, configuration);
            ConfigureLogging(context, configuration, hostingEnvironment);
        }

        public override void OnApplicationInitialization(ApplicationInitializationContext context)
        {
            var app = context.GetApplicationBuilder();
            var env = context.GetEnvironment();

            app.UseCorrelationId();
            app.UseStaticFiles();
            app.UseRouting();
            app.UseAuthentication();
            app.UseMultiTenancy();
            app.UseAbpRequestLocalization();
            app.UseAuthorization();

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseSwagger();
                app.UseSwaggerUI(options =>
                {
                    options.SwaggerEndpoint("/swagger/v1/swagger.json", "Matrix.Notification API V1");
                    options.RoutePrefix = string.Empty;
                });
            }
            else
            {
                app.UseExceptionHandler("/Error");
            }

            app.UseAuditing();
            app.UseAbpSerilogEnrichers();
            app.UseConfiguredEndpoints();
        }

        private void ConfigureAuthentication(ServiceConfigurationContext context, IConfiguration configuration)
        {
            context.Services.AddAuthentication()
                .AddJwtBearer(options =>
                {
                    options.Authority = configuration["AuthServer:Authority"];
                    options.RequireHttpsMetadata = Convert.ToBoolean(configuration["AuthServer:RequireHttpsMetadata"]);
                    options.Audience = configuration["AuthServer:Audience"];
                });
        }

        private void ConfigureLocalization()
        {
            Configure<AbpLocalizationOptions>(options =>
            {
                options.Languages.Add(new LanguageInfo("en", "English", "en"));
                options.Languages.Add(new LanguageInfo("zh-Hans", "简体中文", "zh-Hans"));
            });
        }

        private void ConfigureSwagger(ServiceConfigurationContext context, IConfiguration configuration)
        {
            context.Services.AddAbpSwaggerGenWithOAuth(
                configuration["AuthServer:Authority"],
                new Dictionary<string, string>
                {
                    {"Matrix.Notification", "Matrix.Notification API"}
                },
                options =>
                {
                    options.SwaggerDoc("v1", new OpenApiInfo
                    {
                        Title = "Matrix.Notification API",
                        Version = "v1",
                        Description = "Matrix Framework 通知服务API文档",
                        Contact = new OpenApiContact
                        {
                            Name = "Matrix Framework Team",
                            Email = "support@matrix-framework.com"
                        },
                        License = new OpenApiLicense
                        {
                            Name = "MIT License",
                            Url = "https://opensource.org/licenses/MIT"
                        }
                    });

                    options.DocInclusionPredicate((docName, description) => true);
                    options.CustomSchemaIds(type => type.FullName);
                });
        }

        private void ConfigureCors(ServiceConfigurationContext context, IConfiguration configuration)
        {
            context.Services.AddCors(options =>
            {
                options.AddPolicy("Default", builder =>
                {
                    builder
                        .WithOrigins(
                            configuration["App:CorsOrigins"]
                                .Split(",", StringSplitOptions.RemoveEmptyEntries)
                                .Select(o => o.RemovePostFix("/"))
                                .ToArray()
                        )
                        .WithAbpExposedHeaders()
                        .SetIsOriginAllowedToAllowWildcardSubdomains()
                        .AllowAnyMethod()
                        .AllowAnyHeader();
                });
            });
        }

        private void ConfigureHealthChecks(ServiceConfigurationContext context, IConfiguration configuration)
        {
            context.Services.AddHealthChecks()
                .AddCheck<NotificationHealthCheck>("notification")
                .AddDbContextCheck<NotificationDbContext>("notification_database");

            if (configuration.GetValue<bool>("HealthChecks:EnableRedis"))
            {
                context.Services.AddHealthChecks()
                    .AddRedis(configuration["ConnectionStrings:Redis"]!, "redis");
            }

            if (configuration.GetValue<bool>("HealthChecks:EnableRabbitMQ"))
            {
                // 可以添加RabbitMQ健康检查
            }
        }

        private void ConfigureDataProtection(ServiceConfigurationContext context, IConfiguration configuration)
        {
            var dataProtectionBuilder = context.Services.AddDataProtection().SetApplicationName("Matrix.Notification");

            if (configuration.GetValue<bool>("DataProtection:EnableRedis"))
            {
                var redis = context.GetRequiredService<IDistributedCache>();
                dataProtectionBuilder.PersistKeysToStackExchangeRedis(() =>
                    (redis as StackExchange.Redis.IDatabase)!,
                    new StackExchange.Redis.RedisKey("Matrix.Notification:DataProtection:Keys"));
            }
        }

        private void ConfigureDistributedCache(ServiceConfigurationContext context, IConfiguration configuration)
        {
            if (configuration.GetValue<bool>("DistributedCache:EnableRedis"))
            {
                context.Services.AddStackExchangeRedisCache(options =>
                {
                    options.Configuration = configuration["ConnectionStrings:Redis"];
                    options.InstanceName = "Matrix.Notification";
                });
            }
            else
            {
                context.Services.AddDistributedMemoryCache();
            }
        }

        private void ConfigureDatabase(ServiceConfigurationContext context, IConfiguration configuration)
        {
            context.Services.AddAbpDbContext<NotificationDbContext>(options =>
            {
                options.AddDefaultRepositories(includeAllEntities: true);
            });

            Configure<AbpDbContextOptions>(options =>
            {
                options.Configure dbContextConfigurationContext =>
                {
                    if (configuration.GetValue<bool>("Database:UseInMemory"))
                    {
                        dbContextConfigurationContext.UseInMemoryDatabase("Matrix.Notification");
                    }
                    else
                    {
                        dbContextConfigurationContext.UseNpgsql(
                            configuration.GetConnectionString("Default")
                        );
                    }
                });
            });
        }

        private void ConfigureAuditing(ServiceConfigurationContext context, IConfiguration configuration)
        {
            Configure<AbpAuditingOptions>(options =>
            {
                options.IsEnabledForGetRequests = configuration.GetValue<bool>("Auditing:IsEnabledForGetRequests", false);
                options.IsEnabledForAnonymousRequests = configuration.GetValue<bool>("Auditing:IsEnabledForAnonymousRequests", false);
                options.ApplicationName = "Matrix.Notification";
                options.EntityHistorySelectors.AddAllEntities();
            });
        }

        private void ConfigureLogging(ServiceConfigurationContext context, IConfiguration configuration, IWebHostEnvironment env)
        {
            context.Services.AddLogging(builder =>
            {
                builder.ClearProviders();

                if (env.IsDevelopment())
                {
                    builder.AddConsole();
                    builder.AddDebug();
                }

                // 添加Serilog
                builder.AddSerilog(dispose: true);
            });

            // 配置Serilog
            Log.Logger = new LoggerConfiguration()
                .ReadFrom.Configuration(configuration)
                .ReadFrom.Services(context.Services)
                .Enrich.FromLogContext()
                .Enrich.WithProperty("ApplicationName", "Matrix.Notification")
                .Enrich.WithProperty("Environment", env.EnvironmentName)
                .Enrich.WithProperty("MachineName", Environment.MachineName)
                .Enrich.WithProperty("ProcessId", Environment.ProcessId)
                .Enrich.WithProperty("ThreadId", Environment.CurrentManagedThreadId)
                .Enrich.WithCorrelationIdHeader()
                .MinimumLevel.Information()
                .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
                .MinimumLevel.Override("Volo.Abp", LogEventLevel.Information)
                .WriteTo.Console(
                    outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] [{SourceContext}] [{TenantId}] [{UserId}] {Message:lj}{NewLine}{Exception}")
                .WriteTo.File(
                    path: "logs/notification-.log",
                    rollingInterval: RollingInterval.Day,
                    retainedFileCountLimit: 30,
                    outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} {Level:u3}] [{SourceContext}] [{TenantId}] [{UserId}] {Message:lj}{NewLine}{Exception}")
                .WriteTo.Seq(configuration["Seq:ServerUrl"], restrictedToMinimumLevel: LogEventLevel.Information)
                .CreateLogger();

            // 配置结构化日志
            context.Services.AddAbpSerilog();
        }
    }
}